/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * 知启蒙Servlet兼容库（zhiqim_servlet）在LGPL3.0协议下开源：https://www.zhiqim.com/gitcan/zhiqim/zhiqim_servlet.htm
 *
 * This file is part of [zhiqim_servlet].
 * 
 * [zhiqim_servlet] is free software: you can redistribute
 * it and/or modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * [zhiqim_servlet] is distributed in the hope that it will
 * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with [zhiqim_servlet].
 * If not, see <http://www.gnu.org/licenses/>.
 */
package org.zhiqim.servlet;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.servlet.ServletContext;

import org.zhiqim.httpd.context.ZmlContextConstants;
import org.zhiqim.kernel.Global;
import org.zhiqim.kernel.Service;
import org.zhiqim.kernel.Zhiqim;
import org.zhiqim.kernel.config.Config;
import org.zhiqim.kernel.config.Item;
import org.zhiqim.kernel.constants.ZhiqimConstants;
import org.zhiqim.kernel.logging.Log;
import org.zhiqim.kernel.logging.LogFactory;
import org.zhiqim.kernel.util.Files;
import org.zhiqim.kernel.util.Strings;
import org.zhiqim.kernel.util.Validates;

/**
 * Zhiqim启动关闭控制器，startup & shutdown
 *
 * @version v1.0.0 @author zhichenggang 2017-1-8 新建与整理
 */
public class ZhiqimController implements ZmlContextConstants, ZhiqimConstants
{
    private static final Log log = LogFactory.getLog(ZhiqimController.class);
    
    private final ServletContext context;
    
    public ZhiqimController(ServletContext context)
    {
        this.context = context;
    }
    
    /***************************************************************************/
    //启动 & 关闭
    /***************************************************************************/
    
    /** 工程启动 */
    public void startup()
    {
        if (!Zhiqim.chkJavaVersion())
        {//JDK版本需要JDK1.7以上
            System.exit(1);
            return;
        }
        
        if (!loadConfig())
        {//初始化zhiqim.xml配置失败
            System.exit(1);
            return;
        }
        
        if (!loadClassAliasName())
        {//加载别称失败
            System.exit(1);
            return;
        }
        
        if (!Zhiqim.createService())
        {//创建服务失败
            System.exit(1);
            return;
        }
    }
    
    /** 工程退出 */
    public void shutdown()
    {
        log.info("系统[%s]-版本[%s]正在退出...", Global.getName(), Global.getVersion());
        
        //循环退出服务
        List<Service> serviceList = Global.getServiceListAll();
        for (int i=serviceList.size()-1;i>=0;i--)
        {
            //清理操作，从最后一个开始
            try
            {
                Service service = serviceList.get(i);
                service.destroy();
            }
            catch(Exception e)
            {
                log.info("系统[%s]-版本[%s]清理时有异常情况发生，由开发人员定位，本地不打印日志", Global.getName(), Global.getVersion());
            }
        }

        log.info("系统[%s]-版本[%s]退出完成...", Global.getName(), Global.getVersion());
    }
    
    /***************************************************************************/
    //启动的私有方法
    /***************************************************************************/
    
    /** 初始化配置文件 */
    private boolean loadConfig()
    {
        //先检查xml再检查ini
        String path = null;
        if (Files.exists(getRealPath(Z_CONF_ZHIQIM_XML)))
            path = getRealPath(Z_CONF_ZHIQIM_XML);
        else if (Files.exists(getRealPath(Z_CONF_ZHIQIM_INI)))
            path = getRealPath(Z_CONF_ZHIQIM_INI);
        
        if (path == null)
        {
            log.error("配置文件[/conf/zhiqim.xml]和[/conf/zhiqim.ini]无需配置");
            return true;
        }
        
        try
        {
            Config config = new Config(Z_NAME, path).load();
            
            //1.1检查[boot]下name,version,port三个字段是否必须
            if (!config.hasGroup(Z_BOOT) || !config.hasItem(Z_BOOT, Z_ITEM_NAME) || !config.hasItem(Z_BOOT, Z_ITEM_VERSION) || !config.hasItem(Z_BOOT, Z_ITEM_PORT))
            {
                log.error("配置文件[boot]组中name,version,port必须的，请配置好再启动");
                return false;
            }
            
            //1.2把配置加到全局表中
            String result = Global.addConfig(config);
            if (result != null)
            {
                log.error("根配置文件[%s]在这里[%s]有配置错误", path, result);
                return false;
            }
            
            //2.判断是否需要加载其他的配置文件
            if (config.hasGroup(Z_CONFIG))
            {
                Collection<Item> itemList = config.getItemList(Z_CONFIG);
                for (Item item : itemList)
                {//2.1循环加载
                    Config conf = new Config(item.getKey(), getRealPath(item.getString())).load();
                    
                    //2.2加到全局表中，失败退出
                    result = Global.addConfig(conf);
                    if (result != null)
                    {
                        log.error("配置文件[%s]存在和别的配置文件相同的配置组[%s]", item.getString(), result);
                        return false;
                    }
                }
            }

            return true;
        }
        catch(Exception e)
        {
            log.error("初始化配置文件时异常", e);
            return false;
        }
    }
    
    /** 加载类短别称 */
    private boolean loadClassAliasName()
    {
        List<String> pathList = new ArrayList<>();
        String classPath = getRealPath("/WEB-INF/classes");
        if (Files.exists(classPath))
            pathList.add(classPath);
        
        String libPath = getRealPath("/WEB-INF/lib");
        File libDir = new File(libPath);
        if (libDir.isDirectory()&& libDir.canRead())
        {
            String[] paths = libDir.list(new FilenameFilter() {public boolean accept(File dir, String name){return Strings.endsWithIgnoreCase(name, ".jar") || Strings.endsWithIgnoreCase(name, ".zip");}});
            for (String path : paths)
                pathList.add(getRealPath("/WEB-INF/lib/" + path));
        }
        
        return Zhiqim.loadClassAliasName(pathList);
    }
    
    /** 获取上下文环境对应的真实路径 */
    private String getRealPath(String path)
    {
        path = Files.toLinuxPath(Strings.trim(path));
        if (Validates.isEmpty(path) || "/".equals(path))
            return context.getRealPath("");
        
        return context.getRealPath(path);
    }
}
